/*
    格式化字符串类 以及 格式化子项类
    格式化字符串类：将给定的格式转换成一个一个的格式化子项, 将所有的格式化子项存储在一个数组中，通过遍历该数组，调用格式化子项，完成字符串拼接
    格式化子项类：用于将日志消息拼接到以字符串后面，以便形成一个特定格式的格式化字符串
*/

#pragma once
#include <memory>
#include <vector>
#include <sstream>
#include <utility>
#include <ctime>
#include "message.hpp"
#include "level.hpp"

namespace logging
{
    /*
        格式化消息子项，根据不同格式化字符的样式，按照该样式顺序，依次将日志消息内容拼接成一个特定格式的字符串
        时间、等级、线程ID、文件名、行号、日志器名称、日志有效载荷、制表符、换行符、其他的
    */
    class formatItem
    {
    public:
        using s_ptr = std::shared_ptr<formatItem>;
        virtual void format(std::ostream &out, logMessage &msg) = 0;
    };

    // 时间子项
    class timeFormatItem : public formatItem
    {
    public:
        timeFormatItem(const std::string &fmt = "%Y%m%d%H:%M:%S") : time_fmt(fmt) {}
        void format(std::ostream &out, logMessage &msg) override
        {
            struct tm timeset;                  // 时间信息集合，包含年月日，时分秒
            localtime_r(&msg._ctime, &timeset); // 用于将时间戳，转化为时间集合
            char s[32] = {0};
            strftime(s, sizeof(s), time_fmt.c_str(), &timeset); // 将时间结合按照格式转换为字符串
            out << s;
        }

    private:
        // 时间有不同的输出格式，比如：时分秒/年月日时分秒/等等
        // 所以要根据不同的时间格式，来追加不同的时间字符串
        std::string time_fmt;
    };

    // 等级子项
    class rankFormatItem : public formatItem
    {
    public:
        void format(std::ostream &out, logMessage &msg) override { out << logLevel::toString(msg._rank); }
    };

    // 线程ID子项
    class threadFormatItem : public formatItem
    {
    public:
        void format(std::ostream &out, logMessage &msg) override { out << msg._tid; }
    };

    // 文件名子项
    class fileFormatItem : public formatItem
    {
    public:
        void format(std::ostream &out, logMessage &msg) override { out << msg._src_file; }
    };

    // 行号子项
    class lineFormatItem : public formatItem
    {
    public:
        void format(std::ostream &out, logMessage &msg) override { out << msg._line; }
    };

    // 日志器名称子项
    class loggerFormatItem : public formatItem
    {
    public:
        void format(std::ostream &out, logMessage &msg) override { out << msg._logger; }
    };

    // 有效载荷子项
    class payloadFormatItem : public formatItem
    {
    public:
        void format(std::ostream &out, logMessage &msg) override { out << msg._payload; }
    };

    // 制表符子项
    class tabFormatItem : public formatItem
    {
    public:
        void format(std::ostream &out, logMessage &msg) override { out << "\t"; }
    };

    // 换行符子项
    class newlineFormatItem : public formatItem
    {
    public:
        void format(std::ostream &out, logMessage &msg) override { out << "\n"; }
    };

    // 其他子项
    class otherFormatItem : public formatItem
    {
    public:
        otherFormatItem(const std::string &str) : _str(str) {}
        void format(std::ostream &out, logMessage &msg) override { out << _str; }

    private:
        std::string _str;
    };

    /*
        格式化字符串类，用于将日志消息，按照一定规则的格式字符样式，生成一个格式化字符串
        %d 表示日期 ,包含子格式 {%H:%M:%S}
        %t 表示线程ID
        %c 表示日志器名称
        %f 表示源码文件名
        %l 表示源码行号
        %p 表示日志级别
        %T 表示制表符缩进
        %m 表示主体消息
        %n 表示换行
    */
    class formatter
    {
    public:
        using s_ptr = std::shared_ptr<formatter>;
    public:
        formatter(const std::string &pattern = "[%d{%Y%m%d-%H:%M:%S}][%t][%c][%f:%l][%p]%T%m%n") :_pattern(pattern) { parsePattern(); }

        // 将日志内容，按照规定好的格式，进行字符串拼接，最后返回一个格式化字符串
        std::string format(logMessage &msg)
        {
            std::stringstream ss;
            format(ss, msg);
            return ss.str();
        }
        void format(std::ostream &out, logMessage &msg)
        {
            for (auto item : _items)
            {
                item->format(out, msg);
            }
        }
    private:
        // 对格式化字符串进行解析，按照格式化字符的样式，填写子项数组
        bool parsePattern()
        {
            //std::cout << "进行格式字符串解析" << std::endl;
            // abcde[%d{%H:%M:%S}][%p][%c]%T%m%n}
            size_t pos = 0;
            std::string val, key;
            std::vector<std::pair<std::string, std::string>> elem;
            while (pos < _pattern.size()) {
                // 1. 找%，%之前的都是原始字符串
                size_t percent_pos = _pattern.find('%', pos);
                if (percent_pos == std::string::npos) {
                    // 到了字符串结尾，都没有找到%，此时这些部分都是原始字符串
                    // 添加之后，直接退出循环
                    val = _pattern.substr(pos);
                    if(!val.empty()){
                        elem.emplace_back("", val);
                        val.clear();
                    }
                    break;
                }

                // 找到了%，保存原始字符串
                if(percent_pos > pos) {
                    val = _pattern.substr(pos, percent_pos - pos);
                    elem.emplace_back("", val);
                    val.clear();
                }

                // 2. 到了这里说明找到了%，接下来判断%后面的是什么字符
                pos = percent_pos + 1; // pos指向%下一个位置
                if(pos >= _pattern.size()) {
                    std::cerr << "%后无字符，格式错误" << std::endl;
                    return false; // 如果%后面没有字符了，说明该格式字符串不符合规范，直接返回false
                }
                
                key = _pattern[pos]; // %后面的字符
                if(key == "%") {
                    elem.emplace_back("", "%");
                    pos += 1;
                    continue;
                }

                // 到这里，说明%后面是一个格式字符 即key
                // 继续判断格式化字符后面是否有子格式{}
                pos += 1; // pos指向格式化字符的后面
                val.clear();
                if(pos < _pattern.size() && _pattern[pos] == '{') {
                    // 这里，说明格式化字符后面还有子格式 pos指向{
                    pos += 1; // pos 指向{后的第一个字符
                    while(pos < _pattern.size() && _pattern[pos] != '}') {
                        val.push_back(_pattern[pos++]);
                    }
                    if(pos == _pattern.size()) {
                        std::cerr << "子格式{}匹配失败" << std::endl;
                        return false;
                    }
                    elem.emplace_back(key, val);
                    pos += 1;
                }
                else {
                    // 格式化字符后面没有其他元素了 ,或者后面不是子格式
                    elem.emplace_back(key, "");
                    key.clear();
                }
            }
            for(auto e : elem) {
                //std::cout << e.first << ":" << e.second << std::endl;
                _items.emplace_back(createItem(e.first, e.second));
            }
            return true;
        }

        // 根据格式化字符，来创建对应的格式化子项
        formatItem::s_ptr createItem(const std::string &key, const std::string &val)
        {
            if (key == "d")
                return std::make_shared<timeFormatItem>(val);
            if (key == "t")
                return std::make_shared<threadFormatItem>();
            if (key == "c")
                return std::make_shared<loggerFormatItem>();
            if (key == "f")
                return std::make_shared<fileFormatItem>();
            if (key == "l")
                return std::make_shared<lineFormatItem>();
            if (key == "p")
                return std::make_shared<rankFormatItem>();
            if (key == "T")
                return std::make_shared<tabFormatItem>();
            if (key == "m")
                return std::make_shared<payloadFormatItem>();
            if (key == "n")
                return std::make_shared<newlineFormatItem>();
            if(key == "")  
                return std::make_shared<otherFormatItem>(val);
            std::cerr << "非法格式字符->" << "%" << key << std::endl;
            abort(); 
        }

    private:
        std::string _pattern; // 格式化规则字符串---格式化字符样式
        std::vector<formatItem::s_ptr> _items;
    };
}